﻿using System.Collections.Generic;
using System.Linq;
using Ewk.MachineLearning.Genetic.Programming.Engine;
using Ewk.MachineLearning.Genetic.Programming.Program;
using Ewk.MachineLearning.Genetic.Programming.Program.Functions;
using Ewk.MachineLearning.Genetic.Programming.Program.Terminals;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.MachineLearning.Genetic.Programming.UnitTests.Program
{
    [TestClass]
    public class PopulationFactoryUnitTest
    {
        [TestMethod]
        public void A_NodeTree_With_GenerationMethod_Full_Can_Be_Represented_As_A_Readable_String()
        {
            const int maxDepth = 4;
            const int expectedOutput = 1;

            var population = GetPopulation(maxDepth, expectedOutput).ToList();

            foreach (var individual in population)
            {
                var readableString = individual.ToString();
                Assert.IsInstanceOfType(readableString, typeof(string));
            }
        }

        private static IEnumerable<Node> GetPopulation(int maxDepth, dynamic expectedOutput)
        {
            var problemDefinition = GetProblemDefinition(expectedOutput);
            var engineConfiguration = new EngineConfiguration {MaxDepth = maxDepth, PopulationSize = 64};

            return new PopulationFactory(engineConfiguration, problemDefinition).CreateInitialPopulation();
        }

        private static ProblemDefinition GetProblemDefinition(dynamic expectedOutput)
        {
            var variables = new List<Variable>
                {
                    new Variable(typeof (int), "A"),
                    new Variable(typeof (int), "B"),
                    new Variable(typeof (string), "C"),
                };

            var testSet = new List<Exemplar>
                                {
                                    new Exemplar(new List<dynamic> {1, 2, "3"}, expectedOutput),
                                    new Exemplar(new List<dynamic> {2, 3, "5"}, expectedOutput),
                                    new Exemplar(new List<dynamic> {3, 5, "7"}, expectedOutput),
                                    new Exemplar(new List<dynamic> {5, 7, "11"}, expectedOutput),
                                    new Exemplar(new List<dynamic> {7, 11, "13"}, expectedOutput),
                                };

            var functionFactory = new FunctionFactory();

            return new ProblemDefinition(testSet, variables, null)
                {
                    Functions = new List<Function>
                        {
                            functionFactory.CreateNumericToNumericFunction(
                                objects => objects.Aggregate((o1, o2) => o1 + o2),
                                2,
                                "+"),
                            functionFactory.CreateNumericToNumericFunction(
                                objects => objects.Aggregate((o1, o2) => o1*o2),
                                2,
                                "*"),
                            functionFactory.CreateNumericToStringFunction(
                                objects => objects.Single().ToString(),
                                1,
                                "ToString"),
                        },
                };
        }
    }
}